#include <iostream>

#ifdef __APPLE__
        #include <OpenGL/gl.h>
        #include <OpenGL/glu.h>
        #include <GLUT/glut.h>
#else
	#ifdef _WIN32
        #include "../glut.h"
	#else
        #include <GL/gl.h>
        #include <GL/glu.h>
        #include <GL/glut.h>
	#endif
#endif

#include "../inc/Point.h"
#include "../inc/Polygone.h"
#include "../inc/Image.h"
#include "../inc/OutilsSouris.h"
#include "../inc/Segment.h"
#include "../inc/plateau.h"
#include "../inc/Defines.h"

using namespace std;

/* ************************************************************************ */
/* ***						VARIABLES GLOBALES							*** */
/* ************************************************************************ */

int					__winX = 800;					/* Largeur de la fenetre glut */
int					__winY = 600;					/* Hauteur de la fenetre glut */
float				__zoomX = 1.0;					/* Valeur de zoom de l'image selon l'axe Ox */
float				__zoomY = 1.0;					/* Valeur du zoom de l'image selon l'axe Oy */

Plateau				__plateau;						/* Le plateau de jeu que l'on cherche à créer puis enregistrer */
Case*				__case = new Case();			/* La case que l'on est en train de créer. Une fois qu'elle est ajoutée au plateau elle est réinitialisée pour être réutilisée */
Image				__imagePlateau;					/* Image sur laquelle on crée le plateau et qui servira d'image pour ce plateau */
Image				__pionOrc;						/* Image d'un pions Orc */

int					__caseCliquee = -1;				/* Entier qui indique si l'on a cliqué sur une case, si c'est le cas, il est différent de -1 (c'est le numéro de la case cliquée) */
bool				__clicDroit = false;			/* Booléen qui indique si le clic droit est enfoncé */
bool				__clicMilieu = false;			/* Booléen qui indique si le clic du milieu est enfoncé */
Point*				__pointSousSouris = NULL;		/* Pointeur vers le point sous la souris, vaut null si aucun point n'est sous la souris */
Point*				__pointClique = NULL;			/* Pointeur vers le point sur lequel on a cliqué (clic milieu) */
Point*				__pointSnap = NULL;				/* Pointeur vers le point à snapper lors de l'édition des cases (clic milieu) */

int					__menu;							/* Identifiant du menu principal glut */

/* ************************************************************************ */
/* ***					PROTOTYPES DES FONCTIONS							*** */
/* ************************************************************************ */

void	keyboardGL(unsigned char _k, int _x, int _y);							/* Callback glut pour le clavier */
void	keyboardSpecialGL(int _k, int _x, int _y);
void	mouseGL(int _button, int _state, int _x, int _y);
void	motionGL(int _x, int _y);
void	passiveMotionGL(int _x, int _y);
void	displayGL();
void	reshapeGL(int _w, int _h);
void	choixType ( int _i );
void	choixNbPions( int _i );
void	choixPrincipal(int _i);
int		main(int _argc, char ** _argv);

/* ************************************************************************ */
/* ***						CORPS DU PROGRAMME							*** */
/* ************************************************************************ */

void keyboardGL(unsigned char _k, int _x, int _y)
{
	if ( _k == 'i' )
		__plateau.initNeigbourhood();
		
	switch ( _k )
	{
		case ' ' :
			/* Si la case à ajouter contient moins de deux points, on ne l'ajoute pas, sinon oui */
			if ( __case->getNbPts() > 2 )
			{
				__plateau.addCase( __case );
				__case->clear();
			}
			break;
			
		default : 
			break;
	}
	
	if (__caseCliquee != -1 )
	{	
		if ( _k == '+' )
			__plateau.setCasePts( __caseCliquee, __plateau.getCaseNbPions(__caseCliquee) + 1 );
		
		if ( _k == '-' && __plateau.getCaseNbPions(__caseCliquee) > 0 )
			__plateau.setCasePts( __caseCliquee, __plateau.getCaseNbPions(__caseCliquee) - 1 );
		
		if ( _k == 'p' )
			__plateau.setCaseType( __caseCliquee, PLAINE );
		
		if ( _k == 'm' )
			__plateau.setCaseType( __caseCliquee, MONTAGNE );
		
		if ( _k == 's' )
			__plateau.setCaseType( __caseCliquee, MARAIS );
	}
	
	glutPostRedisplay();
}

void keyboardSpecialGL(int _k, int _x, int _y)
{
	switch ( _k )
	{
		default : 
			break;
	}
	
	glutPostRedisplay();
}

void mouseGL(int _button, int _state, int _x, int _y)
{
	switch ( _button )
	{
		case GLUT_LEFT_BUTTON :
			if ( _state == GLUT_DOWN )
			{
				/* On ajoute le nouvea point au plateau et à la case */
				__case->addPoint( __plateau.newPoint( new Point ( _x, __winY - _y ) ) );
			}
			break;
		
		case GLUT_RIGHT_BUTTON : /* Inutile si on laisse le menu attaché au clic droit de la souris: glut ignorera le callback pour le clic droit */
			if ( _state == GLUT_DOWN )
			{
				__clicDroit = true;
				
				/* On regarde si on a cliqué dans une case. Dans ce cas la, __caseCliquee sera different de -1 */
				__caseCliquee = __plateau.contains( new Point ( _x, __winY - _y) );
				if ( __caseCliquee != -1 ) cout<<"Case cliquee est bord : "<<__plateau.getBord(__caseCliquee)<<endl;
			}
			
			else
				__clicDroit = false;
			
			break;
			
		case GLUT_MIDDLE_BUTTON :
			if ( _state == GLUT_DOWN )
			{
				__clicMilieu = true;
			}
			
			else
				__clicMilieu = false;
			
			break;
			
		default : 
			break;
	}
	
	glutPostRedisplay();
}

void motionGL(int _x, int _y)
{	
	if ( __clicMilieu && __pointSousSouris != NULL )
	{
		/* On regarde si on doit snapper le point à un point déjà existant */
		__pointSnap = __plateau.pointClic( new Point ( _x, __winY - _y ), __pointSousSouris );
		
		if ( __pointSnap != NULL && __pointSnap != __pointSousSouris )
		{
			__case->replacePoint( __pointSousSouris, __pointSnap );
			__plateau.replacePoint( __pointSousSouris, __pointSnap );
			__pointSousSouris = __pointSnap;
		}
		
		else
			__pointSousSouris->setXY( _x, __winY - _y);
	}
	
	glutPostRedisplay();
}

void passiveMotionGL(int _x, int _y)
{	
	/* On vérifie constemment s'il existe un point sous la souris et qu'il est différent d'un point qu'on a deja snappé */
	if ( __clicMilieu )
		__pointSousSouris = __plateau.pointClic( new Point ( _x, __winY - _y), __pointSnap );
	
	else
		__pointSousSouris = __plateau.pointClic( new Point (_x, __winY - _y) );

	glutPostRedisplay();
}

void displayGL()
{
	/* Pour la transparence */
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	
	glClear(GL_COLOR_BUFFER_BIT);
	
	__imagePlateau.display();
	
	/* On affiche tous les points et les contours des cases du plateau */
	__plateau.displayAll();
	
	/* On affiche consecutivement : les countours, l'interieur, le premier et le dernier point de la case que l'on édite */
	__case->display(1,0,0);
	__case->displayFilled(1,0,0,0.3);
	if ( (int)__case->getNbPts() > 0 ) { __case->getPoint(0)->display(1,1,1); __case->getPoint( (int)__case->getNbPts()-1 )->display(0,1,1); }
	
	/* On affiche en jaune le point sous la souris si il existe */
	if ( __pointSousSouris != NULL )
		__pointSousSouris->display(1,1,0);
	
	/* On affiche en bleu la case cliquée s'il y'en a une, son type actuel, et ses voisins en vert (ses voisins doivent être initialisés au préalable) */
	if ( __caseCliquee != -1 )
	{
		__plateau.displayCase( __caseCliquee, 0,0,1,0.3 );
		__plateau.displayCaseType( __caseCliquee );
		__plateau.displayCasePions( __caseCliquee, __pionOrc );
		__plateau.displayNeighbourhood( __caseCliquee, 0,1,0 );
	}
	
	glFlush();
}

void reshapeGL(int _w, int _h)
{
	/* On met à jour la position des points du plateau */
	__plateau.resize(__winX, __winY, _w, _h);
	
	/* On met à jour les valeur de zoom de l'image pour q'elle occupe toute la fenetre */
	__zoomX = ((float)_w/(float)__imagePlateau.getWidth());
	__zoomY = ((float)_h/(float)__imagePlateau.getHeight());
	
	/* On met à jour les valeurs de __winX et __winY */
	__winX = _w;
	__winY = _h;
	
	/* Puis les instruction "classiques" glut */
	glViewport (0,0,_w,_h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0,_w,0,_h);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void menuGL ( int _state )
{
	cout<<"Dans le callback menu"<<endl;
//		__caseCliquee = __plateau.contains( new Point ( _x, __winY - _y) );
}

void choixType ( int _i )
{
	if ( __caseCliquee != -1 )
	{
		switch ( _i )
		{
			case 0 :
				__plateau.setCaseType( __caseCliquee, PLAINE );
				break;
				
			case 1 :
				__plateau.setCaseType( __caseCliquee, MONTAGNE );
				break;
				
			case 2 :
				__plateau.setCaseType( __caseCliquee, MARAIS );
				break;
				
			default :
				break;
		}
	}
}

void choixNbPions ( int _i )
{
	if ( __caseCliquee != -1 )
	{
		/* A IMPLEMENTER EN MEME TEMPS QUE L'AJOUT DE L'ATTRIBUT DANS LA CLASSE CASE */
	}
}

void choixPrincipal ( int _i )
{	
	switch ( _i )
	{
		case 0 :
			/* __plateau.enregistre */
			break;
			
		case 1 :
			exit(0);
			break;
			
		default :
			break;
	}
	glutPostRedisplay();
}

void creermenu ()
{
	int type;
	int nbPions;
	
	/* On crée un premier menu (qui deviendra un sous menu) d'identifiant type et qui appellera la fonction choixType lorsqu'une de ses entrees sera selectionnee */
	type = glutCreateMenu( choixType );
	
	/* Puis on y ajoute des entrées */
	glutAddMenuEntry("Plaine",0);
	glutAddMenuEntry("Montagne",1);
	glutAddMenuEntry("Marais",2);
	
	nbPions = glutCreateMenu( choixNbPions );
	glutAddMenuEntry("1",0);
	glutAddMenuEntry("2",1);
	glutAddMenuEntry("3",2);
	
	__menu = glutCreateMenu( choixPrincipal );
	glutAddSubMenu("Choix du type de la case", type);
	glutAddSubMenu("Choix du nombre de pions sur la case", nbPions);
	glutAddMenuEntry("Enregistrer le plateau de jeu", 0 );
	glutAddMenuEntry("Quitter l'editeur - Ne sauvegarde pas", 1 );
	
	glutSetMenu( __menu );
	glutAttachMenu(GLUT_RIGHT_BUTTON);
}

int main(int _argc, char ** _argv)
{
	int posX, posY;					/* Position initiale de la fenetre glut a l'ecran */
	
	if ( _argc < 2 )
	{
		cout<<"Usage : \""<<_argv[0]<<"\" chemin_vers_image.ppm"<<endl;
		/* On charge l'image de la map par defaut */
		__imagePlateau.loadPPM("default.ppm");
	}
	
	else
		/* On charge l'image de la map choisie par l'utilisateur */
		__imagePlateau.loadPPM(_argv[1]);
	
	/* On charge ensuite les images des pions : */
	__pionOrc.loadPPM("PionsOrc.ppm");
	
	/* On initialise la case d'édition */
	__case->clear();
	
	/* On calcule la taille initiale de la fenêtre */
	__winX = __imagePlateau.getWidth();
	__winY = __imagePlateau.getHeight();
	
	glutInit(&_argc, _argv);
	
	posX = (glutGet(GLUT_SCREEN_WIDTH ) -__winX) / 2;
	posY = (glutGet(GLUT_SCREEN_HEIGHT) -__winY) / 2;
	
	glutInitWindowSize(__winX+1,__winY+1);
	glutInitWindowPosition(posX, posY);
	
	glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE );
	glutCreateWindow("Small Fight - Map Editor");
	
	/* On crée le menu du clic droit */
	/*creermenu();*/
	
	glutDisplayFunc(displayGL);
	glutReshapeFunc(reshapeGL);
	
	/* On "attache" les fonctions callback */
	glutKeyboardFunc(keyboardGL);
	glutSpecialFunc(keyboardSpecialGL);
	glutMouseFunc(mouseGL);
	glutMotionFunc(motionGL);
	glutPassiveMotionFunc(passiveMotionGL);
	/*glutMenuStateFunc( menuGL );*/
	
        glutMainLoop();
	
	return 0;
}
